IAccountManagementPOST Apex Interface

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

Create Operation Use Cases 

Use CaseHook(s)DescriptionExample ScenariosBenefit
Default or Enrich Fields Before CreationtransformRequestNormalizes the incoming request and supplies default, derived, or contextual values before validation and mutation processing begins.- Apply a default Account status or type
- Normalize names, phone numbers, and addresses
- Derive the region from the country code
- Add a source-system value from the request context
Reduces client payload complexity and ensures consistent Account data at creation.
Enforce Business RulesapplyCustomValidationsEvaluates the request against organization-specific rules before the create mutation is built. Invalid requests are rejected early with a clear error response.- Prevent duplicate Account names
- Restrict creation to permitted Account types
- Require registration details for business Accounts
- Validate regional or lifecycle requirements
Rejects invalid requests early and prevents inconsistent or noncompliant Accounts from being created.
Relax or Tighten Built-In ValidatorsconfigureDefaultValidationsEnables, disables, or adjusts built-in validation flags according to the organization’s Account creation process.- Require additional mandatory fields
- Relax validations for trusted internal channels
- Enable stricter address or identifier checks
- Apply different validation policies by Account type or region
Adapts mandatory-field and validation rules to organization-specific processes without changing the shared create implementation.
Auto-Populate or Override Mutation FieldscustomiseMutationPayloadAdds or replaces fields in the GraphQL mutation payload before execution, including organization-specific values that are not represented in the TMF schema.- Set internal classification or segmentation fields
- Add a source-system or correlation ID
- Populate audit and provenance fields
- Override a derived lifecycle status
Persists organization-specific fields beyond the standard TMF schema while preserving the shared API contract.
Create Related Records AtomicallycustomiseMutationPayload (insertGraphQlSnippet)Inserts an additional GraphQL operation into the create mutation so related or junction records are created together with the Account.- Create Account-Contact junctions
- Add relationship roles
- Create related-party associations
- Establish Account hierarchy links
Keeps custom and related data consistent with the Account while reducing additional API calls and partial-write risks.
Create an Audit Trail or Trigger Downstream ProcessinghandlePostOperationRuns after the Account creation mutation succeeds and uses the operation result to perform compliance logging, publish events, or invoke downstream integrations.- Write an Account-creation audit record
- Publish an Account-created event
- Synchronize the Account with billing or CRM systems
- Trigger onboarding, compliance, or notification workflows
Supports compliance and integration side effects while keeping them separate from the core creation operation.

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 

Default accountType when not provided.

1global class AccountMgmtTransformRequestPostExt implements comms_apex_ext.IAccountManagementPOST {
2    global static Map<String, Object> transformRequest(Map<String, Object> context) {
3        if (context == null) {
4            return context;
5        }
6        Object requestBodyObj = context.get('requestBody');
7        if (!(requestBodyObj instanceof Map<String, Object>)) {
8            return context;
9        }
10        Map<String, Object> requestBody = (Map<String, Object>) requestBodyObj;
11
12
13        // Inject a default accountType if the client did not send one.
14        if (!requestBody.containsKey('accountType')) {
15            requestBody.put('accountType', 'Retail');
16        }
17        context.put('requestBody', requestBody);
18        return context;
19    }
20    // ... other hooks return null / empty map ...
21}

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 

Allow contacts without a contact name while preserving required validations.

1global class AccountMgmtValidationConfigPostExt implements comms_apex_ext.IAccountManagementPOST {
2    global static Map<String, Boolean> configureDefaultValidations(
3        Map<String, Boolean> defaultValidationConfiguration,
4        Map<String, Object> context
5    ) {
6        // Keep mandatoryFieldsValidation and relatedPartyValidation ON (defaults);
7        // turn OFF only contactValidation.
8        return new Map<String, Boolean>{
9            'contactValidation' => false
10        };
11    }
12    // ... other hooks return null / empty map ...
13}

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 

Reject reserved name prefixes.

1global class AccountMgmtCustomValidationPostExt implements comms_apex_ext.IAccountManagementPOST {
2    global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {
3        Map<String, Object> requestBody = (Map<String, Object>) context.get('requestBody');
4        Map<String, Object> result = new Map<String, Object>();
5        if (requestBody != null && requestBody.containsKey('name')) {
6            String name = (String) requestBody.get('name');
7            if (name != null && name.startsWith('INVALID_')) {
8                result.put('validationStatus', 'fail');
9                result.put('validationMessage',
10                    'Custom validation failed: name cannot start with INVALID_');
11                result.put('validationDetails', new Map<String, Object>{
12                    'rejectedName' => name,
13                    'rule'         => 'RESERVED_PREFIX'
14                });
15                return result;
16            }
17        }
18        result.put('validationStatus', 'pass');
19        return result;
20    }
21    // ... other hooks return null / empty map ...
22}

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 

Add a field to the create mutation.

1global class AccountMgmtMutationPayloadPostExt implements comms_apex_ext.IAccountManagementPOST {
2    global static Map<String, Object> customiseMutationPayload(
3        Map<String, Object> request,
4        Map<String, Object> context
5    ) {
6        Map<String, Object> addFields = new Map<String, Object>();
7        addFields.put('CustomerClass', 'Commercial');
8        Map<String, Object> node = new Map<String, Object>();
9        node.put('path', 'billingAccount');
10        node.put('addFields', addFields);
11        List<Object> nodes = new List<Object>{ node };
12        Map<String, Object> result = new Map<String, Object>();
13        result.put('nodes', nodes);
14        return result;
15    }
16    // ... other hooks return null / empty map ...
17}

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 

Audit-log the created Billing Account.

1global class AccountMgmtPostOpAuditExt implements comms_apex_ext.IAccountManagementPOST {
2    global static Map<String, Object> handlePostOperation(
3        Map<String, Object> graphQLResult,
4        Map<String, Object> tmfResponse,
5        Map<String, Object> context
6    ) {
7        try {
8            // Extract the created BillingAccount id from the mutation result.
9            String createdId = null;
10            if (graphQLResult != null && graphQLResult.get('data') instanceof Map<String, Object>) {
11                Map<String, Object> data  = (Map<String, Object>) graphQLResult.get('data');
12                Map<String, Object> uiapi = (Map<String, Object>) data.get('uiapi');
13                if (uiapi != null && uiapi.get('billingAccount') instanceof Map<String, Object>) {
14                    Map<String, Object> ba     = (Map<String, Object>) uiapi.get('billingAccount');
15                    Map<String, Object> record = (Map<String, Object>) ba.get('Record');
16                    if (record != null) {
17                        createdId = (String) record.get('Id');
18                    }
19                }
20            }
21            System.debug('BillingAccount created via TMF666 POST by '
22                + UserInfo.getName() + ': ' + createdId);
23        } catch (Exception e) {
24            System.debug('Audit logging failed: ' + e.getMessage());
25        }
26        // Return value is ignored for POST.
27        return null;
28    }
29    // ... other hooks return null / empty map ...
30}

Full Implementation Example 

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

1/**
2 * Complete TMF666 Account Management POST API Extension
3 * Demonstrates transformRequest, configureDefaultValidations, applyCustomValidations,
4 * customiseMutationPayload, and handlePostOperation.
5 */
6global class AccountManagementPOSTExtension implements comms_apex_ext.IAccountManagementPOST {
7
8
9    /** HOOK 1: Default accountType when absent */
10    global static Map<String, Object> transformRequest(Map<String, Object> context) {
11        if (context == null) { return context; }
12        Object bodyObj = context.get('requestBody');
13        if (bodyObj instanceof Map<String, Object>) {
14            Map<String, Object> body = (Map<String, Object>) bodyObj;
15            if (!body.containsKey('accountType')) {
16                body.put('accountType', 'Retail');
17            }
18            context.put('requestBody', body);
19        }
20        return context;
21    }
22
23
24    /** HOOK 2: Keep all default validators enabled */
25    global static Map<String, Boolean> configureDefaultValidations(
26        Map<String, Boolean> defaultValidationConfiguration,
27        Map<String, Object> context
28    ) {
29        return new Map<String, Boolean>();
30    }
31
32
33    /** HOOK 3: Reject reserved name prefixes */
34    global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {
35        Map<String, Object> body = (Map<String, Object>) context.get('requestBody');
36        Map<String, Object> result = new Map<String, Object>();
37        if (body != null && body.get('name') instanceof String
38                && ((String) body.get('name')).startsWith('INVALID_')) {
39            result.put('validationStatus', 'fail');
40            result.put('validationMessage', 'name cannot start with INVALID_');
41            return result;
42        }
43        result.put('validationStatus', 'pass');
44        return result;
45    }
46
47
48    /** HOOK 4: Force CustomerClass on the created record */
49    global static Map<String, Object> customiseMutationPayload(
50        Map<String, Object> request,
51        Map<String, Object> context
52    ) {
53        return new Map<String, Object>{
54            'nodes' => new List<Object>{
55                new Map<String, Object>{
56                    'path'      => 'billingAccount',
57                    'addFields' => new Map<String, Object>{ 'CustomerClass' => 'Commercial' }
58                }
59            }
60        };
61    }
62
63
64    /** HOOK 5: Audit (return ignored for POST) */
65    global static Map<String, Object> handlePostOperation(
66        Map<String, Object> graphQLResult,
67        Map<String, Object> tmfResponse,
68        Map<String, Object> context
69    ) {
70        System.debug('BillingAccount created via TMF666 POST by ' + UserInfo.getName());
71        return null;
72    }
73
74
75    /** NOT APPLICABLE for POST — never invoked */
76    global static Map<String, Object> customiseGraphQLQuery(
77        Map<String, Object> querySpec,
78        Map<String, Object> context
79    ) {
80        return new Map<String, Object>();
81    }
82}

Response Structure 

Request Body with All Attributes 

1{
2  "@type": "BillingAccount",
3  "name": "Billing Account",
4  "state": "Active",
5  "accountType": "Commercial",
6  "billStructure": {
7    "@type": "BillStructure",
8    "presentationMedia": [{ "@type": "BillPresentationMedia", "name": "EBill" }],
9    "format": { "@type": "BillFormat", "name": "Summary Bill" },
10    "cycleSpecification": { "@type": "BillingCycleSpecification", "frequency": "Monthly" }
11  },
12  "taxExemption": [{
13    "certificateNumber": "TAX-12345",
14    "reason": "Lower Income Group",
15    "startDateTime": "2025-01-01T00:00:00.000Z",
16    "endDateTime": "2027-12-31T00:00:00.000Z"
17  }],
18  "relatedParty": [
19    {
20      "role": "Financially Responsible Party",
21      "@type": "RelatedPartyRefOrRelatedPartyRoleRef",
22      "partyOrPartyRole": {
23        "@type": "PartyRefOrPartyRoleRef",
24        "@referredType": "Organization",
25        "id": "001xx000003HP8BAAW",
26        "name": "Test Acme Corp"
27      }
28    }
29  ],
30  "contact": [
31    {
32      "contactName": "Jane Doe",
33      "contactType": "Bill To",
34      "contactMedium": [
35        { "@type": "EmailContactMedium", "emailAddress": "jane.doe@acme.com" },
36        { "@type": "PhoneContactMedium", "phoneNumber": "+1-555-123-4567" },
37        { "@type": "FaxContactMedium", "faxNumber": "+1-555-987-6543" },
38        {
39          "@type": "GeographicAddressContactMedium",
40          "street1": "123 Main St", "street2": "Suite 400",
41          "city": "San Francisco", "stateOrProvince": "CA",
42          "postCode": "94105", "country": "US"
43        }
44      ]
45    }
46  ]
47}

Response (HTTP 201 Created) 

1{
2  "id": "15ixx0000004N64AAE",
3  "href": "/connect/comms/accountmanagement/v5/billingaccount/15ixx0000004N64AAE",
4  "name": "Billing Account",
5  "state": "Active",
6  "accountType": "Commercial",
7  "lastUpdate": "2026-04-26T15:54:53.000Z",
8  "billStructure": {
9    "format": { "name": "Summary Bill", "@type": "BillFormat" },
10    "presentationMedia": [{ "name": "EBill", "@type": "BillPresentationMedia" }],
11    "cycleSpecification": { "frequency": "Monthly", "@type": "BillingCycleSpecification" },
12    "@type": "BillStructure"
13  },
14  "taxExemption": [
15    {
16      "certificateNumber": "TAX-12345",
17      "validFor": {
18        "startDateTime": "2025-01-01T00:00:00.000Z",
19        "endDateTime": "2027-12-31T00:00:00.000Z"
20      },
21      "reason": "Lower Income Group",
22      "@type": "TaxExemption"
23    }
24  ],
25  "relatedParty": [
26    {
27      "role": "Financially Responsible Party",
28      "partyOrPartyRole": {
29        "id": "001xx000003HP8BAAW",
30        "href": "/services/data/v68.0/sobjects/Account/001xx000003HP8BAAW",
31        "name": "Test Acme Corp",
32        "@type": "PartyRef",
33        "@referredType": "Organization"
34      },
35      "@type": "RelatedPartyRefOrRelatedPartyRoleRef"
36    }
37  ],
38  "contact": [
39    {
40      "id": "003xx000004WtVUAA0",
41      "contactName": "Jane Doe",
42      "contactType": "Bill To",
43      "validFor": { "endDateTime": "2099-12-31T00:00:00.000Z" },
44      "contactMedium": [
45        { "id": "003xx000004WtVUAA0-cm-1", "preferred": true, "emailAddress": "jane.doe@acme.com", "@type": "EmailContactMedium" },
46        { "id": "003xx000004WtVUAA0-cm-2", "phoneNumber": "+1-555-123-4567", "@type": "PhoneContactMedium" },
47        { "id": "003xx000004WtVUAA0-cm-3", "faxNumber": "+1-555-987-6543", "@type": "FaxContactMedium" },
48        {
49          "id": "003xx000004WtVUAA0-cm-4",
50          "city": "San Francisco", "country": "US", "postCode": "94105",
51          "stateOrProvince": "CA", "street1": "123 Main St, Suite 400",
52          "@type": "GeographicAddressContactMedium"
53        }
54      ],
55      "@type": "Contact"
56    }
57  ],
58  "@type": "BillingAccount"
59}