---
schema_version: "1"
id: 3c08fc9cd219fbcbe488bc535edb498b49774e3c708aee747d88f195034207c5
title: ICustomerManagementGET Apex Interface
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/industries-integrations/comms/guides/Extensibility/ICustomerManagementGET.md
canonical_url: https://developer.salesforce.com/docs/industries/communications/guide/ICustomerManagementGET.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 7e7b195d5aee7355bee74adc2ed43d826a6b1cc7d07fcde5cdeaf6b017b2171f
products:
  - Communications Cloud TM Forum API
  - Industries Integrations
---
# ICustomerManagementGET Apex Interface

The `ICustomerManagementGET` Apex interface provides extensibility for TMF629 GET operations through Apex pre- and post-hooks and GraphQL query customization. Implementations can validate incoming requests, modify query structures, and refine the response to support business-specific retrieval rules while maintaining TMF-compliant behavior.

This interface supports the following hooks.

- [transformRequest](#transformrequest)
- [applyCustomValidations](#applycustomvalidations)
- [customiseGraphQLQuery](#customisegraphqlquery)
- [handlePostOperation](#handlepostoperation)

**note:** The `configureDefaultValidations` hook is not used for the Customer Management API. If implemented, the method may be invoked, but its return values are ignored.

## Retrieval Lifecycle Use Cases and Hook Mapping

This table lists common use cases and the hooks required for the GET operation.

| **Use Case**                    | **Hook(s)**                                     | **Description** <div style="width:290px"></div>                                                                                                                                                                  | **Example Scenarios** <div style="width:290px"></div>                                                                                                             | **Benefit** <div style="width:290px"></div>                                                           |
| ------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Role-Based Data Filtering**   | `applyCustomValidations`, `handlePostOperation` | Enforces access rules by validating user permissions before execution and filtering sensitive data after retrieval. Supports role-based visibility at field and record levels.                                   | • Restrict access to VIP or high-value customer profiles• Hide sensitive fields (e.g., credit score) for frontline agents• Enforce region-based access boundaries | Strengthens data governance and ensures secure, compliant access to customer data                     |
| **Custom Field Enrichment**     | `handlePostOperation`                           | Adds calculated or derived fields to API responses without changing the base TMF schema, allowing customers to extend or modify as needed. Enables enrichment using internal business rules or external systems. | • Add customer health score• Flag “preferred customer” status• Include aggregated financial or usage insights                                                     | Delivers richer, more contextual responses while preserving TMF schema integrity                      |
| **Dynamic Query Customization** | `customiseGraphQLQuery`                         | Modifies GraphQL queries dynamically based on client or request context by adding fields, filters, or transformations.                                                                                           | • Mobile app requests only essential fields• Portal requires additional entitlement or hierarchy fields• Apply filters automatically for partner channels         | Supports diverse client requirements using a single API and reduces need for API versioning           |
| **Business Rule Validation**    | `applyCustomValidations`                        | Applies business and eligibility rules before processing requests, stopping invalid operations early in the lifecycle.                                                                                           | • Ensure customer is “active” before retrieving services• Reject unauthorized or unverified channel requests• Enforce lifecycle or dependency checks              | Provides strong business consistency and reduces downstream errors by blocking invalid requests early |

## 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

```
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 001, 003, etc.)
       // If not, assume it's an AccountNumber and resolve it
       if (idValue.length() == 15 || idValue.length() == 18) {
           // Looks like a Salesforce ID, return context as-is
           return context;
       }

       // Query Account objects where AccountNumber matches the provided ID
       List<Account> accounts = [
           SELECT Id, AccountNumber
           FROM Account
           WHERE AccountNumber = :idValue
           LIMIT 1
       ];

       // If found, replace the ID in context with the Salesforce ID
       if (!accounts.isEmpty() && accounts[0].AccountNumber != null) {
           context.put('id', accounts[0].Id);
       }

       return context;
   }
```

## applyCustomValidations

This hook validates custom business logic before retrieving customer record. If validation fails, it rejects the request and returns an error response.

The handler processes return values as follows.

Success:

- Return a map with validationStatus set to "pass" (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

```
public Map<String, Object> applyCustomValidations(Map<String, Object> context) {
       // Input context:
       // {
       //   api: 'CustomerManagement',
       //   version: '4.0',
       //   customerId: '001xx000003DHP',
       //   userId: '005xx000001Sv5',
       //   requiredFields: ['Id', 'Name']
       // }

       String customerId = (String) context.get('customerId');
       String userId = (String) context.get('userId');

       Map<String, Object> validationResult = new Map<String, Object>();

       try {
           // Check if user has access to this customer
           User currentUser = [SELECT Id, Profile.Name FROM User WHERE Id = :userId LIMIT 1];
           Account customer = [SELECT Id, BillingCountry FROM Account WHERE Id = :customerId LIMIT 1];

           if ('Customer Service Rep'.equals(currentUser.Profile.Name)) {
               // Validate customer is in allowed region
               if ('US'.equals(customer.BillingCountry)) {
                   // PASS: User has access
                   validationResult.put('validationStatus', 'PASS');
                   validationResult.put('validationMessage', 'User has access to this customer');
               } else {
                   // FAIL: User does not have access to this region
                   validationResult.put('validationStatus', 'FAIL');
                   validationResult.put('validationMessage', 'User does not have access to customers in this region');
                   validationResult.put('validationDetails', new Map<String, Object>{
                       'denialReason' => 'REGION_RESTRICTION',
                       'userRegion' => 'US',
                       'customerRegion' => customer.BillingCountry
                   });
               }
           } else {
               // PASS: Other profiles have unrestricted access
               validationResult.put('validationStatus', 'PASS');
               validationResult.put('validationMessage', 'User profile has unrestricted access');
           }
       } catch (Exception e) {
           // FAIL: Error during validation
           validationResult.put('validationStatus', 'FAIL');
           validationResult.put('validationMessage', 'Validation error: ' + e.getMessage());
           validationResult.put('validationDetails', new Map<String, Object>{
               'errorType' => e.getTypeName()
           });
       }

       // Return validation result directly (not wrapped in 'result' key)
       return validationResult;
   }

```

## customiseGraphQLQuery

This hook modifies the GraphQL query before execution to add fields, filters, or transformations. Returns a map with a `spec` key containing a list of `QueryTransformationNode` objects.

## Hook Method

`Map<String, Object> customiseGraphQLQuery(Map<String, Object> graphQLAsMap, Map<String, Object> context)`

## Sample Apex Implementation

```
public Map<String, Object> customiseGraphQLQuery(
        Map<String, Object> graphQLAsMap,
        Map<String, Object> context
    ) {
        // Create transformation specification to add custom fields and filters
        List<Map<String, Object>> transformationNodes = new List<Map<String, Object>>();

        // Node 1: Add custom fields to the Account node
        // Path: uiapi.query.Account (the Account field in the GraphQL query)
        Map<String, Object> addFieldsNode = new Map<String, Object>{
            'path' => 'uiapi.query.Account',
            'addFields' => new List<String>{'CustomField__c', 'IndustrySegment__c', 'Status'}
        };
        transformationNodes.add(addFieldsNode);

        // Node 2: Add filters to the Account query
        // Only retrieve active accounts
        Map<String, Object> filterNode = new Map<String, Object>{
            'path' => 'uiapi.query.Account',
            'addFilters' => new Map<String, Object>{
                'clearExistingFilters' => false,
                'filterGraphQlSnippet' => '{ Status: { eq: "Active" } }'
            }
        };
        transformationNodes.add(filterNode);

        // Return transformation specification
        return new Map<String, Object>{
            'spec' => transformationNodes
        };
    }

```

## handlePostOperation

This hook post-processes and transforms the API response after data retrieval.

## 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
    ) {
        // Input GraphQL result:
        // {
        //   items: [{ Id: '001xx000003DHP', Name: 'Acme', AnnualRevenue: 5000000 }],
        //   count: 1
        // }

        // Input TMF response:
        // {
        //   customer: [{ id: '001xx000003DHP', name: 'Acme', type: 'Customer' }]
        // }

        List<Object> customerList = (List<Object>) constructedTMFResponse.get('customer');

        if (customerList != null && !customerList.isEmpty()) {
            for (Object customerObj : customerList) {
                Map<String, Object> customer = (Map<String, Object>) customerObj;
                String customerId = (String) customer.get('id');

                // Fetch additional data for enrichment
                Account acc = [SELECT Id, AnnualRevenue, NumberOfEmployees FROM Account WHERE Id = :customerId LIMIT 1];

                // Add calculated fields
                if (acc.AnnualRevenue != null) {
                    String revenueSegment = acc.AnnualRevenue > 10000000 ? 'Enterprise' : 'Mid-Market';
                    customer.put('revenueSegment', revenueSegment);
                }

                if (acc.NumberOfEmployees != null) {
                    customer.put('employeeCount', acc.NumberOfEmployees);
                }

                // Add audit information
                customer.put('lastRetrievedAt', DateTime.now().format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\''));
                customer.put('retrievedBy', UserInfo.getName());
            }
        }

        // Return enriched response
        return constructedTMFResponse;

        // Output response:
        // {
        //   customer: [{
        //     id: '001xx000003DHP',
        //     name: 'Acme',
        //     type: 'Customer',
        //     revenueSegment: 'Enterprise',
        //     employeeCount: 500,
        //     lastRetrievedAt: '2024-01-15T10:30:00Z',
        //     retrievedBy: 'John Smith'
        //   }]
        // }
    }

```

## Full Implementation Example

Here's a complete sample Apex implementation using all supported hooks.

```
/**
 * Complete Customer Management GET API Extension
 * Demonstrates all three applicable lifecycle hooks with business logic
 */
public class CustomerManagementGETExtension implements comms_apex_ext.ICustomerManagementGET {

    /**
     * Hook 1: Apply custom validations
     */
    public Map<String, Object> applyCustomValidations(Map<String, Object> context) {
        String customerId = (String) context.get('customerId');
        String userId = (String) context.get('userId');

        Map<String, Object> validationResult = new Map<String, Object>();

        try {
            User currentUser = [SELECT Id, Profile.Name FROM User WHERE Id = :userId LIMIT 1];
            Account customer = [SELECT Id, BillingCountry FROM Account WHERE Id = :customerId LIMIT 1];

            if ('Customer Service Rep'.equals(currentUser.Profile.Name)) {
                if ('US'.equals(customer.BillingCountry)) {
                    validationResult.put('validationStatus', 'PASS');
                    validationResult.put('validationMessage', 'User has access to this customer');
                } else {
                    validationResult.put('validationStatus', 'FAIL');
                    validationResult.put('validationMessage', 'Access denied - customer region not allowed');
                    validationResult.put('validationDetails', new Map<String, Object>{
                        'denialReason' => 'REGION_RESTRICTION'
                    });
                }
            } else {
                validationResult.put('validationStatus', 'PASS');
                validationResult.put('validationMessage', 'User profile has unrestricted access');
            }
        } catch (Exception e) {
            validationResult.put('validationStatus', 'FAIL');
            validationResult.put('validationMessage', 'Validation error: ' + e.getMessage());
        }

        return validationResult;
    }

    /**
     * Hook 2: Customize GraphQL query
     * Uses transformation syntax from GRAPHQL_QUERY_TRANSFORMATION.md
     */
    public Map<String, Object> customiseGraphQLQuery(
        Map<String, Object> graphQLAsMap,
        Map<String, Object> context
    ) {
        List<Map<String, Object>> transformationNodes = new List<Map<String, Object>>();

        // Add custom fields to the Account node
        // Path: uiapi.query.Account (the Account field in the GraphQL query)
        transformationNodes.add(new Map<String, Object>{
            'path' => 'uiapi.query.Account',
            'addFields' => new List<String>{'CustomField__c', 'IndustrySegment__c', 'Status'}
        });

        // Add filters to the Account query
        // Only retrieve active accounts
        transformationNodes.add(new Map<String, Object>{
            'path' => 'uiapi.query.Account',
            'addFilters' => new Map<String, Object>{
                'clearExistingFilters' => false,
                'filterGraphQlSnippet' => '{ Status: { eq: "Active" } }'
            }
        });

        return new Map<String, Object>{ 'spec' => transformationNodes };
    }

    /**
     * Hook 3: Post-process response
     */
    public Map<String, Object> handlePostOperation(
        Map<String, Object> graphQLQueryResultAsMap,
        Map<String, Object> constructedTMFResponse,
        Map<String, Object> context
    ) {
        List<Object> customerList = (List<Object>) constructedTMFResponse.get('customer');

        if (customerList != null) {
            for (Object customerObj : customerList) {
                Map<String, Object> customer = (Map<String, Object>) customerObj;
                String customerId = (String) customer.get('id');

                try {
                    Account acc = [SELECT Id, AnnualRevenue FROM Account WHERE Id = :customerId LIMIT 1];

                    if (acc.AnnualRevenue != null) {
                        customer.put('revenueSegment', acc.AnnualRevenue > 10000000 ? 'Enterprise' : 'Mid-Market');
                    }
                } catch (Exception e) {
                    System.debug('Error enriching customer data: ' + e.getMessage());
                }
            }
        }

        return constructedTMFResponse;
    }
    /**
     * NOT APPLICABLE: configureDefaultValidations is not used for Customer Management API
     * If implemented, this method will be invoked but return values will be ignored.
     */
    public Map<String, Boolean> configureDefaultValidations(
        Map<String, Boolean> defaultValidationConfiguration,
        Map<String, Object> context
    ) {
        // This hook is not applicable for Customer Management API
        // Return the configuration unchanged
        return defaultValidationConfiguration;
    }
}

```

## Response Structure

### Single Customer Response (GraphQL)

```
{
  "id": "001xx000003DHP",
  "name": "Acme Corporation",
  "type": "Customer",
  "href": "/services/data/v60.0/sobjects/Account/001xx000003DHP",
  "account": [
    {
      "id": "001xx000003DHQ",
      "name": "Acme East Division",
      "type": "Account",
      "href": "/services/data/v60.0/sobjects/Account/001xx000003DHQ",
      "description": "Eastern regional division"
    }
  ],
  "contactMedium": [
    {
      "mediumType": "Email",
      "characteristic": {
        "contactType": "Email",
        "emailAddress": "contact@acme.com"
      }
    },
    {
      "mediumType": "Phone",
      "characteristic": {
        "contactType": "Phone",
        "phoneNumber": "+1-555-0100"
      }
    }
  ],
  "engagedParty": {
    "id": "003xx000004MZZ",
    "name": "John Smith",
    "role": "Individual",
    "href": "/services/data/v60.0/sobjects/Contact/003xx000004MZZ"
  },
  "agreement": [
    {
      "id": "800xx000000001",
      "name": "Service Agreement 2024",
      "href": "/services/data/v60.0/sobjects/Contract/800xx000000001"
    }
  ]
}

```

### List Response (GraphQL)

```
{
  "recordList": [
    {
      "id": "001xx000003DHP",
      "name": "Acme Corporation",
      "type": "Customer",
      "href": "/services/data/v60.0/sobjects/Account/001xx000003DHP"
    },
    {
      "id": "001xx000003DHQ",
      "name": "TechCorp Inc",
      "type": "Customer",
      "href": "/services/data/v60.0/sobjects/Account/001xx000003DHQ"
    }
  ],
  "pageInfo": {
    "startCursor": "YXJyYXljb25uZWN0aW9uOjA=",
    "endCursor": "YXJyYXljb25uZWN0aW9uOjE="
  }
}

```

## GraphQL Query Transformations

Query transformations provide the ability to enhance GraphQL queries by adding fields, applying filters, or modifying the sort order, while preserving the original query definition.

**note:** For the Customer Management API, use the `uiapi.query.Account` path to reference the primary Account field.

### Query Enhancements: Fields and Filters

**Original Query (GraphQL):**

```
query {
myAccounts: accounts {
id
name
}
}
```

**Transformation Instructions (JSON):**

```
{
"nodes": [
{
"path": "myAccounts",
"addFields": ["email", "phone"],
"addFilters": {
"clearExistingFilters": true,
"filterGraphQlSnippet": "{ status: { eq: \"ACTIVE\" } }"
}
}
]
}
```

**Result (GraphQL):**

```
query {
myAccounts: accounts(where: { status: { eq: "ACTIVE" } }) {
id
name
email
phone
}
}
```

### Query Enhancements: Fields, Filters, and Ordering

**Original Query (GraphQL):**

```
query {
myAccounts: accounts {
id
name
}
}
```

**Transformation Instructions (JSON):**

```
{
"nodes": [
{
"path": "myAccounts",
"addFields": ["revenue", "industry"],
"addFilters": {
"clearExistingFilters": true,
"filterGraphQlSnippet": "{
and: [
{ status: { eq: \"ACTIVE\" } },
{ revenue: { gte: 1000000 } }
]
}"
},
"orderBy": {
"clearExistingOrderBy": true,
"orderGraphQlSnippet": "{ revenue: { order: DESC } }"
}
}
]
}
```

**Result (GraphQL):**

```
query {
myAccounts: accounts(
where: {
and: [
{ status: { eq: "ACTIVE" } },
{ revenue: { gte: 1000000 } }
]
},
orderBy: { revenue: { order: DESC } }
) {
id
name
revenue
industry
}
}
```

### Query Enhancements: Multiple Query Paths with Different Transformations

**Original Query (GraphQL):**

```
query {
myAccounts: accounts {
id
name
myContacts: contacts {
id
name
}
}
}
```

**Transformation Instructions (JSON):**

```
{
"nodes": [
{
"path": "myAccounts",
"addFields": ["email"],
"addFilters": {
"clearExistingFilters": true,
"filterGraphQlSnippet": "{ status: { eq: \"ACTIVE\" } }"
}
},
{
"path": "myAccounts.myContacts",
"addFields": ["email", "phone"],
"orderBy": {
"clearExistingOrderBy": true,
"orderGraphQlSnippet": "{ name: { order: ASC } }"
}
}
]
}
```

**Result (GraphQL):**

```
query {
myAccounts: accounts(where: { status: { eq: "ACTIVE" } }) {
id
name
email
myContacts: contacts(orderBy: { name: { order: ASC } }) {
id
name
email
phone
}
}
}
```

### Query Enhancements: Fields and Snippets

**Original Query (GraphQL):**

```
query {
myAccounts: accounts {
id
name
}
}
```

**Transformation Instructions (JSON):**

```
{
"nodes": [
{
"path": "myAccounts",
"addFields": ["email"]
},
{
"path": "metrics",
"insertGraphQlSnippet": "{
metrics: systemMetrics {
totalCount
activeCount
lastSyncTime
}
}"
}
]
}
```

**Result (GraphQL):**

```
query {
myAccounts: accounts {
id
name
email
}
metrics: systemMetrics {
totalCount
activeCount
lastSyncTime
}
}
```
