IIndividualPartyManagementGET Apex Interface

The IIndividualPartyManagementGET Apex interface provides extensibility for 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.

Retrieval Lifecycle Use Cases and Hook Mapping 

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

Use CaseHook(s)DescriptionExample ScenariosBenefit
External-ID ResolutiontransformRequestTransforms legacy, external, or customer-specific identifiers into the canonical request format expected by the API. This allows existing clients to continue using familiar identifiers while keeping the underlying service contract consistent.- Resolve a legacy customer key to { id }
- Translate an account number into an internal resource ID
- Normalize identifiers received from partner or migration systems
Preserves backward compatibility and simplifies integration with legacy or external systems.
Role-Based and Regional Access ControlapplyCustomValidationsApplies authorization and data-governance rules before executing an operation. Access can be evaluated using the user’s role, region, business unit, channel, or request context.- Restrict access to customers outside the user’s assigned region
- Permit only supervisors to access high-value customer records
- Block partner channels from retrieving internal-only resources
Strengthens data governance and ensures secure, policy-compliant access across users and regions.
Configurable Validation PoliciesconfigureDefaultValidationsEnables, disables, or adjusts standard validation flags according to organization-specific requirements. Validation behavior can be tightened or relaxed without changing the core API implementation.- Require additional identifier validation for regulated organizations
- Disable a non-applicable validation for trusted internal channels
- Enable stricter checks in production while relaxing them in test environments
Provides flexible validation behavior while preserving a reusable and consistent API foundation.
Dynamic Query CustomizationcustomiseGraphQLQueryModifies GraphQL queries before execution by adding fields, filters, relationships, or server-side constraints. Query behavior can be tailored to the client, organization, or request context without changing the base schema.- Add organization-specific custom fields
- Automatically apply regional or tenant filters
- Include entitlement, hierarchy, or relationship data for portal clients
- Request a reduced field set for mobile applications
Supports richer, client-specific data requirements while reducing schema changes and API version proliferation.
Response EnrichmenthandlePostOperationProcesses successful operation results before returning them to the client. It can add derived fields, attach audit metadata, transform values, or combine the response with information from internal or external sources.- Add a calculated customer health score
- Include audit timestamps or processing metadata
- Flag preferred or high-risk customers
- Add aggregated financial, service, or usage insights
Delivers richer and more contextual responses while preserving the integrity of the underlying API schema.

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 an external ID to a Salesforce Contact ID.

1global class IndividualGetTransformRequestExt implements comms_apex_ext.IIndividualPartyManagementGET {
2
3
4    global static Map<String, Object> transformRequest(Map<String, Object> context) {
5        if (context == null) {
6            return context;
7        }
8
9
10        Object idObj = context.get('id');
11        if (!(idObj instanceof String)) {
12            return context;
13        }
14        String idValue = (String) idObj;
15
16
17        // A 15- or 18-character value already looks like a Salesforce Id; leave it as-is.
18        if (idValue.length() == 15 || idValue.length() == 18) {
19            return context;
20        }
21
22
23        // Otherwise treat it as an external key and resolve it to a Contact Id.
24        List<Contact> contacts = [
25            SELECT Id FROM Contact WHERE External_Id__c = :idValue LIMIT 1
26        ];
27        if (!contacts.isEmpty()) {
28            context.put('id', contacts[0].Id);
29        }
30        return context;
31    }
32
33
34    // ... other hooks return null / empty map ...
35}

configureDefaultValidations 

This hook enables, disables, or modifies the built-in validation rules before they execute.

Hook Method 

Map<String, Boolean> configureDefaultValidations(Map<String, Boolean> defaultValidationConfiguration, Map<String, Object> context)

Sample Apex Implementation 

Add a custom flag consumed by applyCustomValidations.

1global class IndividualGetConfigValidationsExt implements comms_apex_ext.IIndividualPartyManagementGET {
2
3
4    global static Map<String, Boolean> configureDefaultValidations(
5        Map<String, Boolean> defaultValidationConfiguration,
6        Map<String, Object> context
7    ) {
8        Map<String, Boolean> overrides = defaultValidationConfiguration != null
9            ? defaultValidationConfiguration.clone()
10            : new Map<String, Boolean>();
11        overrides.put('regionAccessCheck', true);   // your own flag, read in applyCustomValidations
12        return overrides;
13    }
14
15
16    // ... other hooks return null / empty map ...
17}

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 

Role-based/regional access control.

1global class IndividualGetRbacExt implements comms_apex_ext.IIndividualPartyManagementGET {
2
3
4    global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {
5        String individualId = (String) context.get('id');
6        Map<String, Object> result = new Map<String, Object>();
7
8
9        try {
10            User currentUser = [SELECT Id, Profile.Name FROM User WHERE Id = :UserInfo.getUserId() LIMIT 1];
11            if ('Customer Service Rep'.equals(currentUser.Profile.Name) && individualId != null) {
12                Contact c = [SELECT Id, MailingCountry FROM Contact WHERE Id = :individualId LIMIT 1];
13                if (!'US'.equals(c.MailingCountry)) {
14                    result.put('validationStatus', 'fail');
15                    result.put('validationMessage', 'User does not have access to individuals in this region');
16                    result.put('validationDetails', new Map<String, Object>{
17                        'statusCode' => 'BAD_REQUEST',
18                        'errorCode'  => 'REGION_RESTRICTION'
19                    });
20                }
21            }
22        } catch (Exception e) {
23            result.put('validationStatus', 'fail');
24            result.put('validationMessage', 'Validation error: ' + e.getMessage());
25        }
26        return result;   // empty => proceed; validationStatus="fail" => HTTP 400
27    }
28
29
30    // ... other hooks return null / empty map ...
31}

customiseGraphQLQuery 

This hook modifies the GraphQL query before execution to add fields, filters, or transformations.

Hook Method 

Map<String, Object> customiseGraphQLQuery(Map<String, Object> graphQLAsMap, Map<String, Object> context)

Sample Apex Implementation 

Add custom fields and a filter: For Individual GET, target the individualDataalias, which represents the Contact node.

1global class IndividualGetCustomGraphQLExt implements comms_apex_ext.IIndividualPartyManagementGET {
2
3
4    global static Map<String, Object> customiseGraphQLQuery(
5        Map<String, Object> graphQLAsMap,
6        Map<String, Object> context
7    ) {
8        List<Map<String, Object>> nodes = new List<Map<String, Object>>();
9
10
11        // Add custom Contact fields.
12        nodes.add(new Map<String, Object>{
13            'path'      => 'individualData',
14            'addFields' => new List<String>{ 'Department', 'Loyalty_Tier__c' }
15        });
16
17
18        // Only return active contacts (append to any existing WHERE clause).
19        nodes.add(new Map<String, Object>{
20            'path' => 'individualData',
21            'addFilters' => new Map<String, Object>{
22                'clearExistingFilters' => false,
23                'filterGraphQlSnippet' => '{ Active__c: { eq: true } }'
24            }
25        });
26
27
28        return new Map<String, Object>{ 'nodes' => nodes };
29    }
30
31    // ... other hooks return null / empty map ...
32}

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 

Enrich the response in place.

1global class IndividualGetEnrichExt implements comms_apex_ext.IIndividualPartyManagementGET {
2
3
4    global static Map<String, Object> handlePostOperation(
5        Map<String, Object> graphQLQueryResultAsMap,
6        Map<String, Object> constructedTMFResponse,
7        Map<String, Object> context
8    ) {
9        if (constructedTMFResponse != null) {
10            constructedTMFResponse.put('retrievedBy', UserInfo.getName());
11            constructedTMFResponse.put('lastRetrievedAt',
12                DateTime.now().format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\''));
13        }
14        // Returning null keeps the mutated constructedTMFResponse as the final payload.
15        return null;
16    }
17}

Full Implementation Example 

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

1/**
2 * Complete TMF632 Individual (Party Management) GET API extension,
3 * demonstrating all five lifecycle hooks with business logic.
4 */
5global class IndividualPartyManagementGETExtension implements comms_apex_ext.IIndividualPartyManagementGET {
6
7
8    // Hook 1: normalize / resolve the request context.
9    global static Map<String, Object> transformRequest(Map<String, Object> context) {
10        if (context != null) {
11            context.put('transformedBy', 'apex');
12        }
13        return context;
14    }
15
16
17    // Hook 2: keep default validations unchanged (GET has none).
18    global static Map<String, Boolean> configureDefaultValidations(
19        Map<String, Boolean> defaultValidationConfiguration,
20        Map<String, Object> context
21    ) {
22        return new Map<String, Boolean>();
23    }
24
25
26    // Hook 3: block requests for disallowed fields.
27    global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {
28        if (context != null && context.get('fields') instanceof List<Object>) {
29            List<Object> fields = (List<Object>) context.get('fields');
30            if (fields != null && fields.contains('invalidField')) {
31                return new Map<String, Object>{
32                    'validationStatus'  => 'fail',
33                    'validationMessage' => 'Invalid field requested'
34                };
35            }
36        }
37        return new Map<String, Object>();
38    }
39
40
41    // Hook 4: add custom fields + an active-only filter.
42    global static Map<String, Object> customiseGraphQLQuery(
43        Map<String, Object> graphQLAsMap,
44        Map<String, Object> context
45    ) {
46        List<Map<String, Object>> nodes = new List<Map<String, Object>>();
47        nodes.add(new Map<String, Object>{
48            'path'      => 'individualData',
49            'addFields' => new List<String>{ 'Department', 'Loyalty_Tier__c' }
50        });
51        nodes.add(new Map<String, Object>{
52            'path' => 'individualData',
53            'addFilters' => new Map<String, Object>{
54                'clearExistingFilters' => false,
55                'filterGraphQlSnippet' => '{ Active__c: { eq: true } }'
56            }
57        });
58        return new Map<String, Object>{ 'nodes' => nodes };
59    }
60
61
62    // Hook 5: enrich the response in place.
63    global static Map<String, Object> handlePostOperation(
64        Map<String, Object> graphQLQueryResultAsMap,
65        Map<String, Object> constructedTMFResponse,
66        Map<String, Object> context
67    ) {
68        if (constructedTMFResponse != null) {
69            constructedTMFResponse.put('retrievedBy', UserInfo.getName());
70        }
71        return null;
72    }
73}

Response Structure 

Single Individual Response (GraphQL) 

1{
2  "id": "003xx000004MZZ",
3  "href": "/connect/comms/partymanagement/v5/individual/003xx000004MZZ",
4  "@type": "Individual",
5  "@baseType": "Party",
6  "givenName": "John",
7  "familyName": "Smith",
8  "name": "John Smith",
9  "title": "VP of Operations",
10  "birthDate": "1980-04-12",
11  "contactMedium": [
12    { "@type": "EmailContactMedium", "emailAddress": "john.smith@acme.com" },
13    { "@type": "PhoneContactMedium", "phoneNumber": "+1-555-0100" },
14    { "@type": "FaxContactMedium", "faxNumber": "+1-555-0101" },
15    {
16      "@type": "GeographicAddressContactMedium",
17      "street1": "1 Market St",
18      "city": "San Francisco",
19      "stateOrProvince": "CA",
20      "postCode": "94105",
21      "country": "US"
22    }
23  ],
24  "relatedParty": [
25    {
26      "@type": "RelatedPartyRefOrPartyRoleRef",
27      "role": "employer",
28      "partyOrPartyRole": {
29        "id": "001xx000003DHP",
30        "href": "/connect/comms/partymanagement/v5/organization/001xx000003DHP",
31        "name": "Acme Corporation",
32        "@type": "PartyRef",
33        "@referredType": "Organization"
34      }
35    }
36  ]
37}

List Response (GraphQL) 

Default (without pageInfo) — a bare JSON array of Individual objects:

1[
2  {
3    "id": "003xx000004MZZ",
4    "href": "/connect/comms/partymanagement/v5/individual/003xx000004MZZ",
5    "@type": "Individual",
6    "@baseType": "Party",
7    "givenName": "John",
8    "familyName": "Smith",
9    "name": "John Smith"
10  },
11  {
12    "id": "003xx000004Na0",
13    "href": "/connect/comms/partymanagement/v5/individual/003xx000004Na0",
14    "@type": "Individual",
15    "@baseType": "Party",
16    "givenName": "Jane",
17    "familyName": "Doe",
18    "name": "Jane Doe"
19  }
20]

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.

Query Enhancements: Fields and Filters 

Original Query (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5}
6}

Transformation Instructions (JSON):

1{
2"nodes": [
3{
4"path": "myAccounts",
5"addFields": ["email", "phone"],
6"addFilters": {
7"clearExistingFilters": true,
8"filterGraphQlSnippet": "{ status: { eq: \"ACTIVE\" } }"
9}
10}
11]
12}

Result (GraphQL):

1query {
2myAccounts: accounts(where: { status: { eq: "ACTIVE" } }) {
3id
4name
5email
6phone
7}
8}

Query Enhancements: Fields, Filters, and Ordering 

Original Query (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5}
6}

Transformation Instructions (JSON):

1{
2"nodes": [
3{
4"path": "myAccounts",
5"addFields": ["revenue", "industry"],
6"addFilters": {
7"clearExistingFilters": true,
8"filterGraphQlSnippet": "{
9and: [
10{ status: { eq: \"ACTIVE\" } },
11{ revenue: { gte: 1000000 } }
12]
13}"
14},
15"orderBy": {
16"clearExistingOrderBy": true,
17"orderGraphQlSnippet": "{ revenue: { order: DESC } }"
18}
19}
20]
21}

Result (GraphQL):

1query {
2myAccounts: accounts(
3where: {
4and: [
5{ status: { eq: "ACTIVE" } },
6{ revenue: { gte: 1000000 } }
7]
8},
9orderBy: { revenue: { order: DESC } }
10) {
11id
12name
13revenue
14industry
15}
16}

Query Enhancements: Multiple Query Paths with Different Transformations 

Original Query (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5myContacts: contacts {
6id
7name
8}
9}
10}

Transformation Instructions (JSON):

1{
2"nodes": [
3{
4"path": "myAccounts",
5"addFields": ["email"],
6"addFilters": {
7"clearExistingFilters": true,
8"filterGraphQlSnippet": "{ status: { eq: \"ACTIVE\" } }"
9}
10},
11{
12"path": "myAccounts.myContacts",
13"addFields": ["email", "phone"],
14"orderBy": {
15"clearExistingOrderBy": true,
16"orderGraphQlSnippet": "{ name: { order: ASC } }"
17}
18}
19]
20}

Result (GraphQL):

1query {
2myAccounts: accounts(where: { status: { eq: "ACTIVE" } }) {
3id
4name
5email
6myContacts: contacts(orderBy: { name: { order: ASC } }) {
7id
8name
9email
10phone
11}
12}
13}

Query Enhancements: Fields and Snippets 

Original Query (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5}
6}

Transformation Instructions (JSON):

1{
2"nodes": [
3{
4"path": "myAccounts",
5"addFields": ["email"]
6},
7{
8"path": "metrics",
9"insertGraphQlSnippet": "{
10metrics: systemMetrics {
11totalCount
12activeCount
13lastSyncTime
14}
15}"
16}
17]
18}

Result (GraphQL):

1query {
2myAccounts: accounts {
3id
4name
5email
6}
7metrics: systemMetrics {
8totalCount
9activeCount
10lastSyncTime
11}
12}